package epoch.db.gamedb;

import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;

import epoch.db.DB;
import epoch.game.core.serialize.ISerializable;
import epoch.game.core.serialize.IStreamReader;
import epoch.game.core.serialize.IStreamWriter;
import epoch.game.core.support.Param;
import epoch.game.core.util.BeanUtil;

/**
 * 实体基类,对应单表结构
 * <pre>注意与{@link Record}区别开，Record对应的是多表
 * </pre>
 * @author will
 * @date 2016年3月24日 下午2:12:31
 * @param <K>
 */
public abstract class Entity implements ISerializable{
	
	private static final long serialVersionUID = 1L;
	
	//常用sql模板
	protected static final String sql_insert="insert into {0} ({1}) values({2})";
	protected static final String sql_update="update {0} set {1}";
	protected static final String sql_select="select * from {0}";
	protected static final String sql_select_by_cond="select * from {0} where {1}";
	protected static final String sql_select_cols="select {0} from {1}";
	protected static final String sql_select_cols_by_cond="select {0} from {1} where {2}";
	protected static final String sql_delete_by_condition="delete from {0} where {1}";
	protected static final String sql_delete_all="delete from {0}";

	//值
	protected Map<String, Object> attrs = new HashMap<String, Object>();	
	
	/**
	 * Flag of column has been modified. update need this flag
	 */
	private Set<String> modifyFlag;
	
	
	Set<String> getModifyFlag() {
		if (modifyFlag == null) {
			modifyFlag = new HashSet<String>();
		}
		return modifyFlag;
	}

	
	public abstract String tableName();


	@Override
	public void readFrom(IStreamReader reader) throws IOException {
		attrs.clear();
		attrs.putAll(reader.<Map<String, Object>>read());
	}

	@Override
	public void writeTo(IStreamWriter writer) throws IOException {
		writer.write(attrs);
	}

	/**
	 * 设置属性，仅供生成代码调用，不检查table中是否含有该列
	 * @param attr
	 * @param value
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected <E extends Entity> E set0(String attr, Object value){
		attrs.put(attr, value);
		getModifyFlag().add(attr);
		return (E) this;
	}
	
	/**
	 * Set attribute to model.
	 * @param attr the attribute name of the model
	 * @param value the value of the attribute
	 * @return this model
	 * @throws ActiveRecordException if the attribute is not exists of the model
	 */
	@SuppressWarnings("unchecked")
	public <E extends Entity> E set(String attr, Object value) {
		Table table = getTable();
		if (table == null || table.hasColumnLabel(attr)) {
			attrs.put(attr, value);
			getModifyFlag().add(attr);
			return (E) this;
		}
		throw new ActiveRecordException("The attribute name does not exist: " + attr);
	}
	
	/**
	 * Remove attribute of this model.
	 * @param attr the attribute name of the model
	 * @return this model
	 */
	@SuppressWarnings("unchecked")
	public <E extends Entity> E remove(String attr) {
		attrs.remove(attr);
		getModifyFlag().remove(attr);
		return (E)this;
	}
	
	/**
	 * Remove attributes of this model.
	 * @param attrs the attribute names of the model
	 * @return this model
	 */
	@SuppressWarnings("unchecked")
	public <E extends Entity> E remove(String... attrs) {
		if (attrs != null)
			for (String a : attrs) {
				this.attrs.remove(a);
				this.getModifyFlag().remove(a);
			}
		return (E)this;
	}
	
	/**
	 * 获取主键值
	 * @return
	 */
	public Object[] getPrimaryKeys(){
		Table table = getTable();
		if(table==null){
			return null;
		}
		String[] primaryKey = table.getPrimaryKey();
		Object[] keyObjs = new Object[primaryKey.length];
		for(int i=0; i<primaryKey.length; i++){
			if(attrs.containsKey(primaryKey[i])){
				keyObjs[i]=getAttrs().get(primaryKey[i]);
			}
		}
		return keyObjs;
	}
	
	/**
	 * 获取单主键模式的主键值
	 * @return
	 */
	public <K> K getPrimaryKey(){
		Table table = getTable();
		if(table==null){
			return null;
		}
		String[] primaryKey = table.getPrimaryKey();
		if(!attrs.containsKey(primaryKey[0])){
			return null;
		}
		@SuppressWarnings("unchecked")
		K key = (K)getAttrs().get(primaryKey[0]);
		return key;
	}
	
	private Table getTable() {
		return TableMapping.me().getTable(this.tableName());
	}

	public Map<String, Object> getAttrs() {
		return attrs;
	}

	public Record toRecord(){
		Record record = new Record();
		record.setColumns(this.attrs);
		return record;
	}
	
	/**
	 * 持久化到数据库, 对应insert操作
	 * @return
	 */
	public void save(){
		DB.getAgent(this.getClass()).insert(this);
	}
	
	/**
	 * 更新数据
	 * @return
	 */
	public void update(){
		update(false);
	}
	
	public void copyToSaveOrUpdate(){
		DB.getAgent(this.getClass()).saveOrUpdate(BeanUtil.deepClone(this));
	}
	
	public void saveOrUpdate(){
		DB.getAgent(this.getClass()).saveOrUpdate(this);
	}
	
	/**
	 * 更新数据,
	 * @param sync 是否立即将修改同步到数据库
	 * @return
	 */
	public void update(boolean sync){
		DB.getAgent(this.getClass()).update(this);
	}
		
	/**
	 * 根据主键删除记录
	 * @return
	 */
	public void delete(){
		DB.getAgent(this.getClass()).delete(tableName(),getPrimaryKey());
	}

	/**
	 * 将entity的值与当前的对象的值合并
	 * @param entity
	 */
	public void merge(Entity entity) {
		this.attrs.putAll(entity.attrs);
	}
	
	public Map<String, Object> change(Entity entity) {
		if (entity.getClass().equals((Object)this.getClass())) {
			return entity.attrs;
		}
		for (Iterator<Entry<String, Object>> iter = entity.attrs.entrySet().iterator(); iter.hasNext(); ) {
			Entry<String, Object> entry = iter.next();
			if (Objects.equals(entry.getValue(), this.attrs.get(entry.getKey()))) {
				iter.remove();
			}
		}
		return entity.attrs;
	}
	
	public Param toParam() {
		return new Param(this.attrs);
	}
	
	@Override
	public String toString() {
		return this.attrs.toString();
	}
	
}
